﻿using Inaction.Core;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Data.Common;
using System.Linq;

namespace Inaction.Business
{
    public partial class EntityList<T, Detail> : ISavable
        where T : EntityList<T, Detail>, new()
        where Detail : Entity<Detail>, new()
    {
        public virtual DbProvider DbProvider
        {
            get { return Inaction.DbProvider.Instance; }
        }
        #region
        public void DoSave()
        {
            using (var connection = DbProvider.Connection)
            {
                var trans = connection.BeginTransaction();
                try
                {
                    foreach (var item in FirstTransData)
                    {
                        item.Save(trans);
                    }
                    foreach (var item in this)
                    {
                        if (item.IsDirty)
                        {
                            item.Save(trans);
                        }
                    }
                    foreach (var item in this.DeletedList)
                    {
                        item.Save(trans);
                    }
                    foreach (var item in LastTransData)
                    {
                        item.Save(trans);
                    }
                    trans.Commit();
                }
                catch (Exception ex)
                {
                    trans.Rollback();
                    Util.Loger.WriteLog(ex);
                    throw ex;
                }
                finally
                {
                    if (connection.State != System.Data.ConnectionState.Closed)
                        connection.Close();
                }
            }
        }

        protected virtual void OnSaving() { }

        protected virtual void OnSaved() { }

        public void Save()
        {
            OnSaving();
            var error = string.Empty;
            if (CheckErrors(out error))
            {
                throw new Exception(error);
            }
            var list = (T)Inaction.Proxy.Save(GetDirtyList());
            this.ClearDeletedEntites();
            if (AppConfig.Instance.RunLocation == RunLocation.Client)
            {
                this.Clone(list);
                foreach (var item in list.FirstTransData)
                {
                    var temp = list.FirstTransData.FirstOrDefault(p => p.Key.Equals(item.Key));
                    temp.Clone(item);
                }
                foreach (var item in list.LastTransData)
                {
                    var temp = list.FirstTransData.FirstOrDefault(p => p.Key.Equals(item.Key));
                    temp.Clone(item);
                }
            }
            this.ClearTransData();
            this.MarkNone(); 
            OnSaved();
        }

      

        public void Save(IDbTransaction trans)
        {
            if (!this.IsDirty) return;
            System.Diagnostics.Stopwatch watch = new System.Diagnostics.Stopwatch();
            try
            {
                watch.Start();
                foreach (var item in firstTransData)
                {
                    item.Save(trans);
                }
                foreach (var item in this)
                {
                    item.Save(trans);
                }
                foreach (var item in this.DeletedList)
                {
                    item.Save(trans);
                }
                foreach (var item in lastTransData)
                {
                    item.Save(trans);
                }
            }
            catch (Exception)
            {
                throw;
            }
            finally
            {
                watch.Stop();
                string log = typeof(T).FullName + ".Save" + ":" + this.Count + "," + watch.ElapsedMilliseconds + "ms";
                Util.Loger.WriteLog(log);
            }
        }
        #endregion

        #region ISavable 成员

        void ISavable.OnSaving(IDbTransaction trans)
        {
            OnSaving(trans);
        }

        void ISavable.OnSaved(IDbTransaction trans)
        {
            OnSaved(trans);
        }
        protected virtual void OnSaving(IDbTransaction trans) { }
        protected virtual void OnSaved(IDbTransaction trans) { }

        List<Core.ISavable> firstTransData = new List<Core.ISavable>();
        [Browsable(false)]
        public List<Core.ISavable> FirstTransData
        {
            get { return firstTransData; }
        }

        List<Core.ISavable> lastTransData = new List<Core.ISavable>();
        [Browsable(false)]
        public List<Core.ISavable> LastTransData
        {
            get { return lastTransData; }
        } 
        [Browsable(false)]
        public int Key
        {
            get;
            private set;
        }
        public void Clone(IClonable source)
        {
            var t = source as T;
            if (t == null) throw new Exception(Inaction.Resource.Properties.Resources.CloneMustBetweenSameType);
            foreach (var item in t)
            {
                var temp = this.FirstOrDefault(p => p.Key.Equals(item.Key));
                if (temp != null)
                {
                    temp.Clone(item);
                }
                else
                {
                    var newItem = this.AddNew(temp);
                    newItem.Master = this.Master;
                }
            }
        }
        public void ClearTransData()
        {
            this.firstTransData.Clear();
            this.lastTransData.Clear();
            foreach (ISavable item in this)
            {
                item.ClearTransData();
            }
        }

        void IDeletable.ClearDeletedEntites()
        {
            this.ClearDeletedEntites();
        }

        #endregion

        #region 方法
        private T GetDirtyList()
        {
            T list = new T();
            foreach (var item in this)
            {
                if (item.IsDirty)
                    list.Add(item);
            }
            foreach (var item in DeletedEntites)
            {
                list.DeletedEntites.Push(item);
            }
            list.firstTransData = this.firstTransData;
            list.lastTransData = this.lastTransData;
            return list;
        }
        
        public void MarkNone()
        {
            foreach (var item in this)
            {
                item.MarkNone();
                item.PropertyTrack();
            }
        }
        #endregion       
    }
}